# -*- coding: utf-8 -*-
# 这个文件夹用来封装一些常用的操作方法
import random
import time
import allure
from typing import Any
from selenium.common.exceptions import TimeoutException
from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver import Keys, ActionChains
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as ec



class WebKeys:

    def __init__(self,driver):
        self.driver = driver
        self.action = ActionChains(self.driver)
        self.wait = WebDriverWait(self.driver, 100)



    @allure.step("访问网址")
    def open(self,url):
        self.driver.get(url)



    @allure.step("显示等待元素定位")
    def locator_with_wait(self,name,value):
        locator=(name,value)
        el=self.wait.until(ec.visibility_of_element_located(locator))
        return el



    @allure.step("为执行的操作加边框")
    def locator_station(self,element):
        self.driver.execute_script(
            "arguments[0].setAttribute('readonly', arguments[1]);",
            element,
            "borser:2px solid green;"   #绿色边框 2px
        )



    @allure.step("断言")
    def text_wait(self,name,value,txt):
        el = (name,value)
        result = self.wait.until(ec.text_to_be_present_in_element(el, txt))
        return result



    @allure.step("获得结果")
    def text_wait(self,name,value,txt):
        el = (name,value)
        result = self.wait.until(ec.text_to_be_present_in_element(el, txt))
        return result



    @allure.step("获得文本内容结果")
    def get_txt(self,name,value):
        el = (name,value)
        result = self.wait.until(ec.visibility_of_element_located(el)).txt
        return result



    @allure.step("点击")
    def on_click(self,name,value):
        self.locator_with_wait(name, value).click()



    @allure.step("获得返回文本")
    def get_text(self,name,value):
        el = (name, value)
        # result = self.wait.until(ec.visibility_of_element_located(el)).text
        result = self.wait.until(
            lambda x: x.find_element(*el).text
        )
        return result



    @allure.step("读取xpath内容")
    def read(self, xpath: str):
        """读取xpath内容"""
        wait = WebDriverWait(self.driver, 10)

        element = wait.until(ec.presence_of_element_located((By.XPATH, xpath)))

        content = element.text
        return content



    @allure.step("显性等待点击")
    def wait_click(self, xpath: str):
        """显性等待点击"""

        # 使用显式等待等待元素可见
        element = WebDriverWait(self.driver, 90).until(
            ec.visibility_of_element_located((By.XPATH, xpath))
        )
        time.sleep(0.3)
        # 执行点击操作
        element.click()



    @allure.step("点击元素 再进行输入 登录框")
    def input_content(self, xpath: str, value: str = '', call_back: Any = None):
        """ 内容输入
            增加回调函数：触发按键
        """

        # 等待元素出现
        element = WebDriverWait(self.driver, 10).until(
            ec.presence_of_element_located((By.XPATH, xpath))
        )

        # 清空现有值并输入新值
        element.send_keys(Keys.CONTROL, "a")
        element.send_keys(Keys.DELETE)
        time.sleep(0.2)
        self.driver.find_element(By.XPATH, xpath).send_keys(value)
        if call_back:
            time.sleep(0.2)
            element.send_keys(Keys.ENTER)



    @allure.step("等待随机时间")
    def random_time(self, start_time=2, end_time=3):
        """等待随机时间"""
        random_float = round(random.uniform(start_time, end_time), 2)
        time.sleep(random_float)



    @allure.step("切换到指定索引的标签页 -1 最新窗口")
    def change_window(self,n):
        """窗口切换"""
        #获取全部窗口
        self.handles=self.driver.window_handles
        #切换窗口 n代表下标 最新的窗口是-1
        self.driver.switch_to.window(self.handles[n])



    @allure.step("切换到指定索引的标签页  int值为0 最新窗口")
    def switch_to_tab(self, tab_index: int):
        """切换到指定索引的标签页"""

        # 获取所有标签页句柄
        all_handles = self.driver.window_handles
        # 确保标签页索引有效
        if tab_index < len(all_handles):
            # 切换到指定标签页
            self.driver.switch_to.window(all_handles[tab_index])
        else:
            print("标签页索引超出范围")



    @allure.step("鼠标滑动到具体元素")
    def mouse_slide(self,xpath,timeout=10):
        """鼠标滑动到具体元素"""
        # 创建ActionChains对象,将鼠标滑动到目标元素

        element=self.driver.find_element(By.XPATH, xpath)
        actions = ActionChains(self.driver,timeout)
        actions.move_to_element(element).perform()



    @allure.step("判断勾选框 有没有被勾选")
    def is_selected(self,xpath,timeout=10):
        """判断勾选框 有没有被勾选"""
        checkbox = WebDriverWait(self.driver,timeout).until(
            ec.presence_of_element_located((By.XPATH, xpath))
        )
        if not checkbox.is_selected():
            """元素未勾选，正在勾选..."""
            checkbox.click()  # 勾选操作



    @allure.step("鼠标移动到元素上面去")
    def action_mouse_focus(self, xpath: str):
        #鼠标移动到元素上面去
        element = self.driver.find_element(By.XPATH, xpath)

        self.random_time(1, 1.2)
        self.action.move_to_element(element).perform()



    @allure.step("xpath双击")
    def double_click(self, xpath: str):
        """xpath双击"""
        time.sleep(0.2)
        wait = WebDriverWait(self.driver, 10)
        # 等待元素可见
        element = wait.until(ec.visibility_of_element_located((By.XPATH, xpath)))

        self.action.double_click(element).perform()



    @allure.step("切换到指定的iframe")
    def switch_to_frame(self, frame_switch: (int, str)):
        """切换到指定的iframe，可以通过索引、name、id或XPath"""

        if isinstance(frame_switch, int):
            # 通过索引切换
            self.driver.switch_to.frame(frame_switch)
            return True
        elif isinstance(frame_switch, str):
            try:
                # 先尝试通过 name 或 id 切换
                self.driver.switch_to.frame(frame_switch)
                return True
            except Exception:
                # 如果name/id失败，尝试按XPath查找iframe元素
                try:
                    iframe_element = self.driver.find_element(By.XPATH, frame_switch)
                    self.driver.switch_to.frame(iframe_element)
                    return True
                except Exception as e:
                    print(f"切换到 iframe 失败: {e}")
                    return False



    @allure.step("跳出frame")
    def break_frame(self):
        self.driver.switch_to.default_content()



    @allure.step("清除输入框内容，输入内容")
    def set_value_enter(self, xpath: str, new_value: str):
        time.sleep(0.2)
        try:
            # 等待元素出现
            element = WebDriverWait(self.driver, 10).until(
                ec.presence_of_element_located((By.XPATH, xpath))
            )

            # 清空现有值并输入新值
            element.send_keys(Keys.CONTROL, "a")
            element.send_keys(Keys.DELETE)
            time.sleep(0.2)
            element.send_keys(new_value)
            # 使用 ActionChains 模拟按下回车键
            actions = ActionChains(self.driver)
            actions.send_keys(Keys.ENTER)
            actions.perform()
            # 检查更改
            updated_value = element.get_attribute('value')
        except Exception as e:
            print(f'Error: {e}')



    @allure.step("模拟按下回车")
    def enter(self):
        """模拟按下回车"""
        self.action.send_keys(Keys.ENTER)



    @allure.step("鼠标滚动到指定元素")
    def roll_element_visibility(self, xpath: str):
        actions = ActionChains(self.driver)
        element = self.driver.find_element(By.XPATH, xpath)
        self.driver.execute_script("arguments[0].scrollIntoView();", element)
        actions.click(on_element=element)



    @allure.step("页面滚动到底部")
    def scroll_to_bottom(self, xpath, on_scroll=None, lens=10):
        element = self.driver.find_element(By.XPATH, xpath)
        # 聚焦到元素
        self.action.move_to_element(element).click().perform()
        for i in range(lens):
            # 聚焦后触发 END 键
            self.action.send_keys(Keys.PAGE_DOWN).perform()
            time.sleep(2)
            if on_scroll:
                on_scroll()
                time.sleep(3)



    @allure.step("等待随机时间")
    def random_time(self, start_time=2, end_time=3):
        random_float = round(random.uniform(start_time, end_time), 2)
        time.sleep(random_float)



    @allure.step("滚动到指定元素")
    def scrolling_to_elements(self, xpath: str):
        """将元素滚动到窗口可见"""
        element = self.driver.find_element(By.XPATH, xpath)

        self.driver.execute_script("""
                let element = arguments[0];
                // 滚动到元素，并将其滚动到视口的下方位置
                element.scrollIntoView({block: 'center', inline: 'center', behavior: 'smooth'});
            """, element)


    @allure.step("滑动到指定元素")
    def slide_element(self, xpath: str):
        """滑动到指定元素"""
        element = self.driver.find_element(By.XPATH,xpath)

        # 滚动到元素位置（元素顶部对齐视窗顶部）
        self.driver.execute_script("arguments[0].scrollIntoView();", element)

        # 平滑滚动（带动画效果）
        self.driver.execute_script("arguments[0].scrollIntoView({behavior: 'smooth'});", element)

    @allure.step("动态等待 判断 XPath 元素是否存在")
    def is_element_present(self, xpath: str, wait_sleep=10) -> bool:
        """判断 XPath 元素是否存在（在 DOM 中出现），等待最多 wait_sleep 秒"""
        try:
            WebDriverWait(self.driver, wait_sleep).until(
                ec.presence_of_element_located((By.XPATH, xpath))
            )
            return True
        except (TimeoutException, NoSuchElementException):
            return False